//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/lexer/sm_c.h"
#include "sagittarius/io/io.h"
#include <malloc.h>
#include <string.h>
#include <math.h>

#if SAGITTARIUS_LXALPHA_USE_GMP
    #include "sagittarius/gmp/gmp_set.h"
#endif

typedef struct {
    UnicodeCChar* buf;
    size_t cursor;
    size_t buflen;
    
#if SAGITTARIUS_LXALPHA_USE_GMP
    ///@brief Used to prevent multiple emitting of the same symbol
    gmp_set* gs;
#endif
} sm_c_workbuf;

/// sm_c_grow_buf
void sm_c_grow_buf(sm_c_workbuf *wb) {
    size_t newlen = wb->buflen ? (wb->buflen << 2) : 256;
    AT(newlen < 1000000);
    {
        UnicodeCChar* tmp = malloc(newlen*sizeof(UnicodeCChar));
        memcpy(tmp, wb->buf, wb->buflen*sizeof(UnicodeCChar));
        free(wb->buf);
        wb->buf = tmp;
        wb->buflen = newlen;
    }
}

/// sm_c_push_str
void sm_c_push_str(sm_c_workbuf* wb, const char* str) {
    size_t strl = strlen(str)+1; //+1 for null char
    size_t nchr;
    UnicodeCChar* convbuf = malloc(strl*sizeof(strl));
    
    nchr = UTF8_ConvertBuffer(str, convbuf);
    while(wb->cursor + nchr >= wb->buflen)
        sm_c_grow_buf(wb);
    AT(wb->cursor + nchr < wb->buflen, "Unexpected overflow: sm_c_grow_buf failed");
    memcpy(wb->buf+wb->cursor, convbuf, nchr*sizeof(UnicodeCChar));
    wb->cursor += nchr;
    
    free(convbuf);
}

/// sm_c_push_chr
void sm_c_push_chr(sm_c_workbuf* wb, const UnicodeCChar x) {
    while(wb->cursor + 1 >= wb->buflen)
        sm_c_grow_buf(wb);
    AT(wb->cursor + 1 < wb->buflen, "Unexpected overflow: sm_c_grow_buf failed");
    wb->buf[wb->cursor++] = x;
}

/// sm_c_push_strl
void sm_c_push_strl(sm_c_workbuf* wb, const char* __str, int l) {
    char* str = malloc((l+1)*sizeof(char));
    memcpy(str, __str, l);
    str[l] = '\0';
    
    {
        size_t strl = strlen(str)+1; //+1 for null char
        size_t nchr;
        UnicodeCChar* convbuf = malloc(strl*sizeof(strl));
        
        nchr = UTF8_ConvertBuffer(str, convbuf);
        if(wb->cursor + nchr >= wb->buflen)
            sm_c_grow_buf(wb);
        AT(wb->cursor + nchr < wb->buflen, "Unexpected overflow: sm_c_grow_buf failed");
        memcpy(wb->buf+wb->cursor, convbuf, nchr);
        
        free(convbuf);
    }
    
    free(str);
}

void sm_c_push_lexbuf(sm_c_workbuf* wb, constLexBuf buf) {
    char* str;
    size_t buflen=LX_ENCBUFFER_LEN(buf);
    str = malloc(buflen);
    LX_BUFFER_ENC_UTF8(buf, str);
    sm_c_push_str(wb, str);
    free(str);
}

void sm_c_push_uint32(sm_c_workbuf* wb, uint32 n) {
    int mag;
    int i;
    char digits[64];
    if(n == 0) {
        sm_c_push_str(wb, "0");
        return;
    }
    mag = log10((double)n);
    AT(mag < 32);
    for(i=0; i<=mag; ++i) {
        digits[i] = (n / (uint32)pow(10,mag-i))%10 + 0x30;
    }
    digits[i] = '\0';
    sm_c_push_str(wb, digits);
}

void sm_c_push_uint64(sm_c_workbuf* wb, uint64 n) {
    int mag;
    int i;
    char digits[64];
    if(n == 0) {
        sm_c_push_str(wb, "0");
        return;
    }
    mag = log10((double)n);
    AT(mag < 64);
    for(i=0; i<=mag; ++i) {
        digits[i] = (n / (uint64)pow(10,mag-i))%10 + 0x30;
    }
    digits[i] = '\0';
    sm_c_push_str(wb, digits);
}

#if SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_GNUC || SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_MINGW
inline
#endif
double sm_c_log16(double x) {
    return log(x) / log(16.0);
}

void sm_c_push_uint32Hex(sm_c_workbuf* wb, uint32 n) {
    int i;
    char digits[32];
    for(i=0; i<8; ++i) {
        //digits[i] = (n / (uint32)pow(16,mag-i))%16;
        digits[i] = (n >> ((7-i)*4)) & 0xf;
        if(digits[i] < 10)
            digits[i] += 0x30;
        else
            digits[i] += 0x57;
    }
    digits[i] = '\0';
    sm_c_push_str(wb, digits);
}

void sm_c_push_uint64Hex(sm_c_workbuf* wb, uint64 n) {
    int i;
    char digits[64];
    for(i=0; i<16; ++i) {
        digits[i] = (n >> ((15-i)*4)) & 0xf;
        if(digits[i] < 10)
            digits[i] += 0x30;
        else
            digits[i] += 0x57;
    }
    digits[i] = '\0';
    sm_c_push_str(wb, digits);
}

#if SAGITTARIUS_LXALPHA_USE_GMP
void sm_c_push_symbol_rep(sm_c_workbuf* wb, mpz_t x) {
    char str[128];
    mpz_get_str(str, 16, x);
    sm_c_push_str(wb, "0x");
    sm_c_push_str(wb, str);
}

int sm_c_is_symbol_defined(sm_c_workbuf* wb, LXAlphaType x) {
    return gmp_set_check(wb->gs, SG_BIGNUM_GETMPZ(x));
}
#endif

#if SAGITTARIUS_LXALPHA_USE_GMP
    #define SM_C_EMIT_SYMBOL_STR(wb, x) sm_c_push_symbol_rep(wb, x);
#else
    #define SM_C_EMIT_SYMBOL_STR(wb, x) sm_c_push_str(wb, "0x"); sm_c_push_uint64Hex(wb, LX_GET_FIXED_VALUE(x));
#endif

//#if 0
void sm_c_push_ttable_leaf(sm_itable_single* l) {
    sm_tier_type i;
    for(i=0; i<SM_ITABLE_SIZE; ++i) {
        SG_WRITE(1, "    Index[");
        sg_writeUInt64(1, i);
        SG_WRITE(1, "] -> 0x");
        sg_writeUInt64Hex(1, l->itbl[i]);
        SG_WRITE(1, "\n");
    }
}

/*void sm_c_push_ttable_branch(sm_itable_branch* b) {
    sm_tier_type l; //leaf
    for(l=0; l<b->n; ++l) {
        SG_WRITE(1, "  Transition table: Leaf ");
        sg_writeUInt64(1, l);
        SG_WRITE(1, "\n");
        sm_dumpttable_leaf(&b->leafs[l]);
    }
}

void sm_c_push_ttable(sm_itable* tbl) {
    sm_tier_type t;
    for(t=0; t<tbl->n; ++t) {
        sm_c_push_str(&wb, "dfa_ttable_tier");
        sm_c_push_uint64(&wb, t);
        sm_dumpttable_branch(&tbl->b[t]);
    }
}*/

uint32 sm_c_do_ttable_tier(sm_c_workbuf* wb, smrep* sm, sm_itable* tbl, sm_tier_type t) {
    sm_tier_type l; //leaf
    sm_tier_type n;
    sm_tier_type off=0;
    sm_itable_branch* b=&tbl->b[t-1];
    for(n=0; n<b->n; ++n) {
        for(l=0; l<SM_ITABLE_SIZE; ++l) {
            if(off != 0) {
                //break evey 16 vals
                if(!(off % 8))
                    sm_c_push_str(wb,",\n  ");
                else
                    sm_c_push_str(wb,", ");
            }
            sm_c_push_str(wb, "0x");
            if((b->leafs[n].itbl[l] & SM_ITABLE_CONTINUE_FLAG) && t > 1)
                sm_c_push_uint32Hex(wb, (b->n*256 + (b->leafs[n].itbl[l] & SM_LOOKUP_MASK)*256) | SM_ITABLE_CONTINUE_FLAG);
            else
                sm_c_push_uint32Hex(wb, b->leafs[n].itbl[l]);
            //sm_c_push_uint32(wb, b->leafs[n].itbl[l]);
            ++off;
        }
    }
    return off;
}

void sm_c_push_ttables(sm_c_workbuf* wb, smrep* sm) {
    //top-tier table:
    uint32 n;
    uint32 off=0; //offset
    sm_tier_type t;
    //AN(0);
    for(n=0; n<sm->dfa->n; ++n) {
        if(sm->stbl[n]) {
            /*SG_WRITE(1, "Generating C for table ");
            sg_writeUInt64(1, n);
            SG_WRITE(1, "\n");*/
            for(t=sm->stbl[n]->n; t>0; --t) {
                if(n != 0 || t != sm->stbl[n]->n)
                    sm_c_push_str(wb, ",\n");
                sm_c_push_str(wb, "  /* Transitions for tier ");
                sm_c_push_uint64(wb, t);
                sm_c_push_str(wb, ", state ");
                sm_c_push_uint64(wb, n);
                sm_c_push_str(wb, "*/\n  ");
                //sm->stbl[n]->offset=off;
                off += sm_c_do_ttable_tier(wb, sm, sm->stbl[n], t);
                //sm_c_push_str(wb, "\n");
            }
        } else {
            /*SG_WRITE(1, "Not generating C for table ");
            sg_writeUInt64(1, n);
            SG_WRITE(1, "\n");*/
        }
    }
    sm_c_push_str(wb, "\n");
}
//#endif

/// Get the ttable size for a given state
uint32 get_ttable_size_state(smrep* sm, uint32 n) {
    uint32 s=0;
    sm_tier_type t;
    if(sm->stbl[n]) {
        for(t=0; t<sm->stbl[n]->n; ++t) {
            s += sm->stbl[n]->b[t].n*SM_ITABLE_SIZE;
        }
    }
    return s;
}

uint32 get_ttable_size(smrep* sm) {
    uint32 s=0, n;
    for(n=0; n<sm->dfa->n; ++n) {
        s += get_ttable_size_state(sm, n);
    }
    return s;
}

void sm_c_push_indent(sm_c_workbuf* wb, uint32 indent) {
    uint32 i;
    for(i=0; i<indent; ++i)
        sm_c_push_str(wb, " ");
}

void sm_c_emit_identifiers(sm_c_workbuf* wb, smrep* sm, uint32 indent) {
    size_t n;
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "const char* identifiers[");
    sm_c_push_uint64(wb, sm->dfa->s[0]->v->n);
    sm_c_push_str(wb, "] = {\n");
    for(n=0; n<sm->dfa->s[0]->v->n; ++n) {
        if(n!=0)
            sm_c_push_str(wb, ",\n");
        sm_c_push_indent(wb, indent+2);
        sm_c_push_str(wb, "\"");
        if(sm->dfa->s[0]->v->e[n]->id)
            sm_c_push_lexbuf(wb, sm->dfa->s[0]->v->e[n]->id);
        else
            sm_c_push_str(wb, "NO_ID");
        sm_c_push_str(wb, "\"");
    }
    sm_c_push_str(wb, "\n");
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "};\n");
}

/**
 * @brief Pushes a string representing symbol @a x onto the workbuffer
 */
void sm_c_push_alpha(sm_c_workbuf* wb, LXAlphaType x) {
    if(LX_CMPALPHA_EQ_SPC(x, CONST_RE_NULL)) {
        sm_c_push_chr(wb, 0x2205);
        return;
    }
    if(LX_CMPALPHA_EQ_SPC(x, CONST_RE_EMPTY_STR)) {
        sm_c_push_str(wb, "--");
        sm_c_push_chr(wb, 0x1D700);
        sm_c_push_str(wb, "--");
        return;
    }
    if(LX_CMPALPHA_EQ_SPC(x, CONST_RE_ALPHAMAX)) {
        sm_c_push_chr(wb, 0x221E);
        return;
    }
    if(LX_CMPALPHA_EQ_SPC(x, CONST_RE_MAX)) {
        sm_c_push_str(wb, "MAX");
        return;
    }
    if(LX_CMPALPHA_EQ_SPC(x, 0x7f)) {
        sm_c_push_str(wb, "DEL"); //the only non-printable ASCII char above 0x20
        return;
    }
    if(LX_CMPALPHA_GTE_SPC(x, 0x20) && !LX_CMPALPHA_EQ_SPC(x, 0x7f)) {
        sm_c_push_chr(wb, LX_GET_FIXED_VALUE(x));
        return;
    }
    #if SAGITTARIUS_LXALPHA_USE_GMP
    SM_C_EMIT_SYMBOL_STR(wb, SG_BIGNUM_GETMPZ(x))
    #else
    SM_C_EMIT_SYMBOL_STR(wb, x)
    #endif
}

/**
 * @brief Pushes a string representing the charset onto the workbuffer
 */
void sm_c_push_charset(sm_c_workbuf* wb, RE_CharSet* cs) {
    uint32 i;
    sm_c_push_str(wb, "CS{");
    for(i=0; i<cs->n; ++i) {
        if(!LX_CMPALPHA_EQ(cs->ranges[i].l, cs->ranges[i].u)) {
            sm_c_push_alpha(wb, cs->ranges[i].l);
            sm_c_push_str(wb, "-");
            sm_c_push_alpha(wb, cs->ranges[i].u);
        } else {
            sm_c_push_alpha(wb, cs->ranges[i].l);
        }
        if(i<cs->n-1)
            sm_c_push_str(wb, ",");
    }
    sm_c_push_str(wb, "}");
}

#if SAGITTARIUS_LXALPHA_USE_GMP

/**
 * @brief Emits a definition for a variable containing the symbol @a x
 * @details Each definition is emitted only once (otherwise the variable
 * would be multiply defined). This function uses @a wb->gs (type @ref gmp_set
 * ) to determine if a symbol has already been defined.
 */
void sm_c_emit_symbol_def(sm_c_workbuf* wb, LXAlphaType x) {
    if(!sm_c_is_symbol_defined(wb, x)) {
        gmp_set_insert(wb->gs, SG_BIGNUM_GETMPZ(x));
        sm_c_push_str(wb, "LXAlphaType Symbol_");
        SM_C_EMIT_SYMBOL_STR(wb, SG_BIGNUM_GETMPZ(x));
        sm_c_push_str(wb, ";\n");
    }
}

/**
 * @brief Emits definitions for all symbols used
 */
void sm_c_emit_boundary_defs(sm_c_workbuf* wb, smrep* sm, uint32 indent) {
    uint32 n;
    for(n=0; n<sm->dfa->n; ++n) {
        if(!sm->stbl[n]) {
            uint32 r;
            RE_CharSet* cs;
            dfa_outTranLink* l=sm->dfa->s[n]->tbeg;
            while(l) {
                if(l->cur) {
                    cs = l->cur->cs;
                    for(r=0; r<cs->n; ++r) {
                        sm_c_push_indent(wb, indent);
                        sm_c_emit_symbol_def(wb, cs->ranges[r].l);
                        if(!LX_CMPALPHA_EQ(cs->ranges[r].l, cs->ranges[r].u)) {
                            sm_c_push_indent(wb, indent);
                            sm_c_emit_symbol_def(wb, cs->ranges[r].u);
                        }
                    }
                }
                l = l->next;
            }
        }
    }
}

/**
 * @brief Emits initialization code for all symbols used
 */
void sm_c_emit_boundary_init(sm_c_workbuf* wb, smrep* sm, uint32 indent) {
    uint64 n;
    uint32 i;
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "void init_symbols() {\n");
    for(n=0; n<wb->gs->nbuckets; ++n) {
        for(i=0; i<wb->gs->buckets[n].n; ++i) {
            sm_c_push_indent(wb, indent+4);
            sm_c_push_str(wb, "LX_FORCE_INIT(Symbol_");
            SM_C_EMIT_SYMBOL_STR(wb, SG_BIGNUM_GETMPZ(wb->gs->buckets[n].e[i]));
            sm_c_push_str(wb, ");\n");
            sm_c_push_indent(wb, indent+4);
            sm_c_push_str(wb, "LX_ALPHA_SET_SPC(Symbol_");
            SM_C_EMIT_SYMBOL_STR(wb, SG_BIGNUM_GETMPZ(wb->gs->buckets[n].e[i]));
            sm_c_push_str(wb, ", ");
            SM_C_EMIT_SYMBOL_STR(wb, SG_BIGNUM_GETMPZ(wb->gs->buckets[n].e[i]));
            sm_c_push_str(wb, ");\n");
        }
    }
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "}\n");
}

/**
 * @brief Emits cleanup code for all symbols used
 */
void sm_c_emit_boundary_clear(sm_c_workbuf* wb, smrep* sm, uint32 indent) {
    uint32 n;
    uint32 i;
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "void clear_symbols() {\n");
    for(n=0; n<wb->gs->nbuckets; ++n) {
        for(i=0; i<wb->gs->buckets[n].n; ++i) {
            sm_c_push_indent(wb, indent+4);
            sm_c_push_str(wb, "LX_ALPHA_CLEAR(Symbol_");
            SM_C_EMIT_SYMBOL_STR(wb, SG_BIGNUM_GETMPZ(wb->gs->buckets[n].e[i]));
            sm_c_push_str(wb, ");\n");
        }
    }
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "}\n");
}

#endif

/**
 * @brief Emits definitions (in the parser state) for user defined variables
 */
void sm_c_push_uservars(sm_c_workbuf* wb, lexvar* v, uint64 nv, uint32 indent) {
    uint64 i;
    for(i=0;i<nv;++i) {
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "uint32 ");
        sm_c_push_lexbuf(wb, v[i].name);
        sm_c_push_str(wb, "; /* User variable */\n");
    }
}

/**
 * @brief Emits initialization code for user variables
 */
void sm_c_push_init_uservars(sm_c_workbuf* wb, lexvar* v, uint64 nv, uint32 indent) {
    uint64 i;
    for(i=0;i<nv;++i) {
        if(v[i].haveintv) {
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "ps->");
            sm_c_push_lexbuf(wb, v[i].name);
            sm_c_push_str(wb, " = ");
            sm_c_push_uint64(wb, v[i].initval);
            sm_c_push_str(wb, ";\n");
        } else {
            AN(0);
        }
    }
}

/**
 * @brief Emit actions for a state
 * @details When a state accepts, it may perform certain actions,
 * such as calling a user-specified function or simply returning
 * from the main routine with info on which expression was accepted.
 * @param[in] wb Workbuffer
 * @param[in] sm State machine info
 * @param[in] n  Which state accepted?
 * @param[in] i  Which expression was accepted?
 * @param[in] indent Base indentation level of generated code
 */
void sm_c_emit_accept(sm_c_workbuf* wb, smrep* sm, uint32 n, size_t i, uint32 indent) {
    size_t k;
    lex_actchain* c;
    lexaction *act;
    int do_break;
    sm_c_push_indent(wb, indent);
    sm_c_push_str(wb, "/* Accept pattern ");
    sm_c_push_uint64(wb, i);
    sm_c_push_str(wb, " */\n");
    
    c = sm->dfa->s[n]->v->e[i]->act;
    
    for(k=0;k<c->n;++k) {
        act = &c->a[k];
        if(act->type == LEX_ACTION_STOP) {
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "ps->ac = ");
            sm_c_push_uint64(wb, i);
            sm_c_push_str(wb, ";\n");
            //sm_c_push_indent(wb, indent);
            //sm_c_push_str(wb, "++ps->w; /* Advance the cursor so the next entry won't use this char twice */\n");
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "return;\n");
            do_break = 0;
        } else if(act->type == LEX_ACTION_RESET) {
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "/* Reset */\n");
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "ps->s = 0;\n");
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "ps->ac = DFA_ACC_NONE;\n");
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "ps->strt = ps->w;\n");
            do_break = 1;
        } else if(act->type == LEX_VAR_INC) {
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "/* Increment variable */\n");
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "ps->");
            sm_c_push_lexbuf(wb, act->id);
            sm_c_push_str(wb, "++;\n");
            do_break = 1;
        } else if(act->type == LEX_ACTION_NONE) {
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "/* No action */\n");
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "++ps->w;\n");
            do_break = 0;
        }
    }
    if(do_break) {
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "break;\n");
    }
}

/**
 * @brief Emit essential code for a state in the autamaton
 * @details Emits transitions and calls @ref sm_c_emit_accept to emit
 * accepting code where applicable
 * @param[in] wb Workbuffer
 * @param[in] sm State machine info
 * @param[in] n  The for which to emit code
 * @param[in] indent Base indentation level of generated code
 */
void sm_c_emit_state(sm_c_workbuf* wb, smrep* sm, uint32 n, uint32 indent) {
    int emit_else=0;
    //this state uses the table
    RE_CharSet* cs;
    uint32 r;
    int accept=0; //set to 1 for accepting states
    size_t i;
    size_t n_accept; //the index (in the vector) of the expression this state accepts
    dfa_outTranLink* l=sm->dfa->s[n]->tbeg;
    
    if(l && l->cur) {
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "/* State ");
        sm_c_push_uint64(wb, n);
        sm_c_push_str(wb, " has transitions: ");
        while(l) {
            if(l->cur) {
                cs = l->cur->cs;
                sm_c_push_charset(wb, cs);
                sm_c_push_str(wb, " -> ");
                sm_c_push_uint64(wb, l->cur->s);
                if(l->next && l->next->cur)
                    sm_c_push_str(wb, ", ");
            }
            l = l->next;
        }
        sm_c_push_str(wb, " */\n");
    } else {
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "/* State ");
        sm_c_push_uint64(wb, n);
        sm_c_push_str(wb, " has no outbound transitions*/\n");
    }
    //print comments describing which, if any, expressions this state accepts
    for(i=0; i<sm->dfa->s[n]->v->n; ++i) {
        if(sm->dfa->s[n]->v->e[i]->r->nullable) {
            sm_c_push_indent(wb, indent);
            sm_c_push_str(wb, "/* Accepting state for \"");
            sm_c_push_lexbuf(wb, sm->dfa->s[n]->v->e[i]->id);
            sm_c_push_str(wb, "\" */\n");
            accept = 1;
            n_accept = i;
        }
    }
    
    if(sm->stbl[n]) {
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "s = lookup_transition(ps->s, ps);\n");
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "if(s != DFA_INVALID_TRANSITION) {\n");
        sm_c_push_indent(wb, indent+4);
        sm_c_push_str(wb, "ps->s = s;\n");
        sm_c_push_indent(wb, indent+4);
        sm_c_push_str(wb, "++ps->w;\n");
        sm_c_push_indent(wb, indent+4);
        sm_c_push_str(wb, "break;\n");
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "}\n");
    } else {
        l=sm->dfa->s[n]->tbeg;
        while(l) {
            if(l->cur) {
                cs = l->cur->cs;
                sm_c_push_indent(wb, indent);
                sm_c_push_str(wb, "/* Transition for ");
                sm_c_push_charset(wb, cs);
                sm_c_push_str(wb, "*/\n");
                #if 0
                //iterator method
                RE_CharSetIter i=re_CSIStart(cs);
                AT(re_CSIValid(&i), "Unable to emit empty transition");
                do {
                } while(re_CSIValid(&i));
                #endif
                //range method
                for(r=0; r<cs->n; ++r) {
                    sm_c_push_indent(wb, indent);
                    if(emit_else)
                        sm_c_push_str(wb, "else ");
                    #if SAGITTARIUS_LXALPHA_USE_GMP
                    if(!LX_CMPALPHA_EQ(cs->ranges[r].l, cs->ranges[r].u)) {
                        sm_c_push_str(wb, "if(LX_CMPALPHA_LTE(Symbol_");
                        SM_C_EMIT_SYMBOL_STR(wb, cs->ranges[r].l.v);
                        sm_c_push_str(wb, ", *ps->w) && LX_CMPALPHA_LTE(*ps->w, Symbol_");
                        SM_C_EMIT_SYMBOL_STR(wb, cs->ranges[r].u.v);
                    } else {
                        sm_c_push_str(wb, "if(LX_CMPALPHA_EQ(Symbol_");
                        SM_C_EMIT_SYMBOL_STR(wb, cs->ranges[r].l.v);
                        sm_c_push_str(wb, ", *ps->w");
                    }
                    #else
                    if(!LX_CMPALPHA_EQ(cs->ranges[r].l, cs->ranges[r].u)) {
                        sm_c_push_str(wb, "if(LX_CMPALPHA_LTE(");
                        SM_C_EMIT_SYMBOL_STR(wb, cs->ranges[r].l);
                        sm_c_push_str(wb, ", *ps->w) && LX_CMPALPHA_LTE(*ps->w, ");
                        SM_C_EMIT_SYMBOL_STR(wb, cs->ranges[r].u);
                    } else {
                        sm_c_push_str(wb, "if(LX_CMPALPHA_EQ(");
                        SM_C_EMIT_SYMBOL_STR(wb, cs->ranges[r].l);
                        sm_c_push_str(wb, ", *ps->w");
                    }
                    #endif
                    sm_c_push_str(wb, ")) {\n");
                    
                    sm_c_push_indent(wb, indent+4);
                    sm_c_push_str(wb, "ps->s = ");
                    sm_c_push_uint64(wb, l->cur->s);
                    sm_c_push_str(wb, ";\n");
                    sm_c_push_indent(wb, indent+4);
                    sm_c_push_str(wb, "++ps->w;\n");
                    sm_c_push_indent(wb, indent+4);
                    sm_c_push_str(wb, "break;\n");
                    
                    sm_c_push_indent(wb, indent);
                    sm_c_push_str(wb, "}\n");
                    //cs->ranges[r].l
                }
            }
            l = l->next;
        }
    }
    if(accept) {
        //accept for there are no legal transitions
        sm_c_emit_accept(wb, sm, n, n_accept, indent);
    } else {
        //this state is not accepting: if there are no
        //legal transitions we must emit an error
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "/* Reject if there are no legal transitions */\n");
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "ps->ef = 1;\n");
        sm_c_push_indent(wb, indent);
        sm_c_push_str(wb, "return;\n");
    }
}

//state info

#define SM_C_SI_SHIFT_MASK 0xFF
//#define SM_C_SI_USE_TTABLE_FLAG 0x0010

/// dfa2sm_c
void dfa2sm_c(UnicodeCChar** __buf, smrep* sm, lexvar* v, uint64 nv) {
    //dfaState* s;
    uint32 n;
    uint64 state_info;
    uint64 off;
    size_t t;
	#if SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_GNUC || SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_MINGW
    sm_c_workbuf wb = {.buf=NULL, .cursor=0, .buflen=0};
	#else
	sm_c_workbuf wb = {0};
	#endif
    
    AN(__buf);
    AZ(*__buf, "Callee does allocation");
    
    #if SAGITTARIUS_LXALPHA_USE_GMP
    //init gmp set
    wb.gs = gmp_set_rehash(NULL, 64, 16);
    #endif
    
    sm_c_push_str(&wb, "//== AUTOMATICALLY GENERATED =========================================================\n\n");
    

    sm_c_push_str(&wb, "#include \"sagittarius/core/SagittariusCore.h\"\n");
    sm_c_push_str(&wb, "#include \"sagittarius/lexer/lexcore.h\"\n\n");
    sm_c_push_str(&wb, "#include \"sagittarius/io/io.h\"\n\n");
    sm_c_push_str(&wb, "//== BEGINNING OF CODE ===============================================================\n\n\n");
    sm_c_push_str(&wb, "#define SM_START_STATE 0\n");
    sm_c_push_str(&wb, "#define DFA_ACC_NONE 0xffffffff\n");
    sm_c_push_str(&wb, "#define STATE_INFO_SHIFT_MASK 0x");
    sm_c_push_uint64Hex(&wb, SM_C_SI_SHIFT_MASK);
    sm_c_push_str(&wb, "\n#define STATE_INFO_OFFSET_MASK 0x");
    sm_c_push_uint64Hex(&wb, 0xffffffff00000000);
    sm_c_push_str(&wb, "\n\n\n");
    
    //itable flags
    
    sm_c_push_str(&wb, "#define SM_ITABLE_CONTINUE_FLAG 0x");
    sm_c_push_uint32Hex(&wb, SM_ITABLE_CONTINUE_FLAG);
    sm_c_push_str(&wb, "\n");
    sm_c_push_str(&wb, "#define SM_LOOKUP_MASK 0x");
    sm_c_push_uint32Hex(&wb, SM_LOOKUP_MASK);
    sm_c_push_str(&wb, "\n");
    sm_c_push_str(&wb, "#define DFA_INVALID_TRANSITION 0x");
    sm_c_push_uint32Hex(&wb, SM_C_INVALID_TRANSITION);
    sm_c_push_str(&wb, "\n\n\n");
    
    sm_c_push_str(&wb, "\n\n\n");
    
    //Output defines for named patterns
    for(t=0; t<sm->dfa->s[0]->v->n; ++t) {
        if(sm->dfa->s[0]->v->e[t]->id) {
            sm_c_push_str(&wb, "#define TOKEN_");
            sm_c_push_lexbuf(&wb, sm->dfa->s[0]->v->e[t]->id);
            sm_c_push_str(&wb, " ");
            sm_c_push_uint64(&wb, t);
            sm_c_push_str(&wb, "\n");
        }
    }
    
    sm_c_push_str(&wb, "\n\n\n");
    
    sm_c_push_str(&wb, "uint64"); //state info type
    sm_c_push_str(&wb," state_info[");
    sm_c_push_uint64(&wb, sm->dfa->n);
    sm_c_push_str(&wb,"] = {\n  ");
    off = 0;
    for(n=0; n<sm->dfa->n; ++n) {
        if(n != 0) {
            //break evey 4 vals
            if(!(n % 4))
                sm_c_push_str(&wb,",\n  ");
            else
                sm_c_push_str(&wb,", ");
        }
        state_info = 0;
        if(sm->stbl[n]) {
            //state_info |= SM_C_SI_USE_TTABLE_FLAG; //use the ttable
            state_info |= sm->stbl[n]->n-1; //shift level
            state_info |= off << 32;
        }
        sm_c_push_str(&wb,"0x");
        sm_c_push_uint64Hex(&wb, state_info);
        //adjust base offset for state n
        off += get_ttable_size_state(sm, n);
        /*SG_WRITE(1, "off = ");
        sg_writeUInt64(1, off);
        SG_WRITE(1, "\n");*/
    }
    sm_c_push_str(&wb,"};\n\n\n\n");
    
    sm_c_push_str(&wb,
        "typedef struct {\n"
        "    constLexBuf w; /* Position in the input buf */\n"
        "    constLexBuf strt; /* Starting position of accepted pattern (where ac = true) */\n"
        "    uint32 s; /* State */\n"
        "    uint32 ef; /* Error flag */\n"
        "    uint32 ac; /* Accepted pattern */\n"
        );
    
    //User variables
    sm_c_push_uservars(&wb, v, nv, 4);
    
    sm_c_push_str(&wb,
        "} sm_c_parserState;\n"
        );
    
    sm_c_push_str(&wb, "\n\n\n");
    
    sm_c_push_str(&wb,
        "void dfa_init_ps(sm_c_parserState* ps, constLexBuf buf) {\n"
        "    /* Initialize the parser state */\n"
        "    ps->s = 0;\n"
        "    ps->w = buf;\n"
        "    ps->strt = ps->w;\n"
        "    ps->ac = DFA_ACC_NONE;\n"
        "    ps->ef = 0;\n"
        );
    
    //Init for user vars
    sm_c_push_init_uservars(&wb, v, nv, 4);
    
    sm_c_push_str(&wb,
        "}\n"
        );
    
    sm_c_push_str(&wb, "\n\n\n");
    
    sm_c_emit_identifiers(&wb, sm, 0);
    sm_c_push_str(&wb, "\n");
    
    sm_c_push_str(&wb, 
        "void dfa_print_acc_id(uint32 ac) {\n"
        "    sg_writeCStr(1, identifiers[ac]);\n"
        "}\n"
        "\n"
    );
    
    sm_c_push_str(&wb, "\n\n\n");
    
    #if SAGITTARIUS_LXALPHA_USE_GMP
        //symbol values
        sm_c_emit_boundary_defs(&wb, sm, 0);
        
        sm_c_push_str(&wb, "\n\n\n");
        
        //symbol initialization
        sm_c_emit_boundary_init(&wb, sm, 0);
        sm_c_push_str(&wb, "\n\n\n");
        
        //symbol initialization
        sm_c_emit_boundary_clear(&wb, sm, 0);
        sm_c_push_str(&wb, "\n\n\n");
    #endif
    
    sm_c_push_str(&wb, "uint32 ttbl[");
    sm_c_push_uint64(&wb, get_ttable_size(sm));
    sm_c_push_str(&wb, "] = {\n");
    sm_c_push_ttables(&wb, sm);
    
    sm_c_push_str(&wb, "};\n\n\n");
    
    sm_c_push_str(&wb,
        "uint32 lookup_transition(uint32 s, sm_c_parserState* ps) {\n"
        "    LXAlphaType x;\n"
        "    LX_FORCE_INIT(x);\n"
        "    \n"
        "    uint32 shift=state_info[s] & STATE_INFO_SHIFT_MASK;\n"
        "    LXFixedType base=(uint32)((state_info[s] & STATE_INFO_OFFSET_MASK) >> 32);\n"
        "    while(shift) {\n"
        "        LX_ALPHA_SET(x, *ps->w);\n"
        "        LX_ALPHA_RSHIFT_SPC(x, (8*shift));\n"
        /*"        SG_WRITE(1, \"Add \");\n"
        "        sg_writeUInt(1, LX_GET_FIXED_VALUE(x) & 0xff);\n"
        "        SG_WRITE(1, \"\\n\");\n"*/
        "        base = ttbl[base + (LXFixedType)(LX_GET_FIXED_VALUE(x) & 0xff)];\n"
        /*"        SG_WRITE(1, \"Lookup at tier \");\n"
        "        sg_writeUInt(1, shift+1);\n"
        "        SG_WRITE(1, \" for state \");\n"
        "        sg_writeUInt(1, s);\n"
        "        SG_WRITE(1, \" was \");\n"
        "        sg_writeUInt32Hex(1, base);\n"
        "        SG_WRITE(1, \"\\n\");\n"*/
        "        if(base & SM_ITABLE_CONTINUE_FLAG)\n"
        "            base = base & SM_LOOKUP_MASK;\n"
        "        else\n"
        "            return DFA_INVALID_TRANSITION;\n"
        "        --shift;\n"
        "    }\n"
        "    /* Get next state */\n"
        "    LX_ALPHA_SET(x, *ps->w);\n"
        /*"    SG_WRITE(1, \"Add \");\n"
        "    sg_writeUInt(1, LX_GET_FIXED_VALUE(x) & 0xff);\n"
        "    SG_WRITE(1, \"\\n\");\n"*/
        "    base = ttbl[base + (LXFixedType)(LX_GET_FIXED_VALUE(x) & 0xff)];\n"
        /*"    SG_WRITE(1, \"Lookup at tier \");\n"
        "    sg_writeUInt(1, shift+1);\n"
        "    SG_WRITE(1, \" for state \");\n"
        "    sg_writeUInt(1, s);\n"
        "    SG_WRITE(1, \" was \");\n"
        "    sg_writeUInt32Hex(1, base);\n"
        "    SG_WRITE(1, \"\\n\");\n"*/
        "    \n"
        "    LX_ALPHA_CLEAR(x);\n"
        "    \n"
        "    if(base & SM_ITABLE_CONTINUE_FLAG)\n"
        "        base = base & SM_LOOKUP_MASK;\n"
        "    else\n"
        "        return DFA_INVALID_TRANSITION;\n"
        "    \n"
        "    return (uint32)base & SM_LOOKUP_MASK;\n"
        "}\n\n\n"
        );
    
    sm_c_push_str(&wb,
        "void resume_dfa(sm_c_parserState* ps) {\n"
        "    uint32 s;\n"
        "    \n"
        "    while(/* !LX_CMPALPHA_EQ_SPC(*ps->w, '\\0') && */ !ps->ef) {\n"
        "        switch(ps->s) {\n"
        );
    
    for(n=0; n<sm->dfa->n; ++n) {
        sm_c_push_indent(&wb, 10);
        sm_c_push_str(&wb, "case ");
        sm_c_push_uint64(&wb, n);
        sm_c_push_str(&wb, ":\n");
        sm_c_emit_state(&wb, sm, n, 12);
    }
    sm_c_push_str(&wb,
        "        }\n" //switch
        "    }\n"
        "}\n\n\n"
        );
    
    /*sm_c_push_str(&wb,
        "void run_dfa() {\n"
        "    uint32 s = SM_START_STATE;\n"
        "    \n"
        );
    #if SAGITTARIUS_LXALPHA_USE_GMP
    sm_c_push_str(&wb,
        "    init_symbols()\n"
        "    \n");
    #endif
    sm_c_push_str(&wb,
        "    resume_dfa(s);\n"
        "    \n"
        "}\n"
        );*/
    
    sm_c_push_chr(&wb, 0); //terminating null char
    *__buf = wb.buf;
    
    #if SAGITTARIUS_LXALPHA_USE_GMP
    gmp_set_destroy(wb.gs);
    #endif
}
